import cv2
import numpy as np
from utils.prior_box import PriorBox
from utils.box_utils import decode, decode_landm
from utils.py_cpu_nms import py_cpu_nms
from config import cfg_mnet
cfg = cfg_mnet

"""
一、人脸检测流程：
1. 加载模型
2. 图片的预处理
3. 模型前向计算
4. 处理模型输出结果
"""
#填充（扩边）图像（图像补边）即在图像周围绘制边界框
def scale32(image):
    h = 0
    w = 0
    ih,iw,_ = image.shape
    if ih % 32 ==0:
        h = ih
    else:
        h = (int(ih/32)+1)*32
    if iw % 32 ==0:
        w = iw
    else:
        w = (int(iw/32)+1)*32
    new_image = cv2.copyMakeBorder(image,0,h - ih,0,w - iw,cv2.BORDER_CONSTANT)#填充（扩边）图像，在卷积运算或0填充时被用到
    return new_image


def face_det(net,img):
    """
    人脸检测函数，输入图片，输出是脸的位置、关键点
    """
    #1.模型与处理
    h,w,_ = img.shape
    #图片预处理转为浮点型,
    blob = cv2.dnn.blobFromImage(img,ddepth=cv2.CV_32F,mean=[104,117,123],)
    print("blob的数据形状：",blob.shape)
    #2.模型前向计算
    outlayer_names = net.getUnconnectedOutLayersNames()#获取未连接的输出层的名字
    net.setInput(blob)
    landms,conf,loc = net.forward(outlayer_names)
    
    #3.模型输出解码
    priorbox = PriorBox(cfg, image_size=(h, w))
    priors = priorbox.forward()
    #box解码
    boxes = decode(loc[0], priors, cfg['variance'])#从先验框解码为相应对位置坐标
    scale = np.array([w,h,w,h])
    boxes = boxes * scale #相对位置坐标（0~1）  绝对位置坐标
    #关键点解码
    landms = decode_landm(landms[0], priors, cfg['variance'])  #从先验框解码为相应对位置坐标
    scale = np.array([w,h,w,h,w,h,w,h,w,h])
    landms = landms * scale
    #张量转numpy
    scores = conf[0][:, 1] 
    inds = np.where(scores > 0.5)[0]
    boxes = boxes[inds]
    landms = landms[inds]
    scores = scores[inds]
    #5.do NMS(非极大值抑制)去除重复的box
    dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False)
    keep = py_cpu_nms(dets, 0.6)#重复阈值达到0.6认为同一个框
    dets = dets[keep, :]
    landms = landms[keep]
    #最终返回有目标的框和对应的关键点
    return dets,landms
if __name__ == "__main__":
    #加载模型
    net = cv2.dnn.readNet("../models/FaceDetector.onnx")
    #读取图片
    image = cv2.imread("./curve/test.jpg")
    image = scale32(image)
    dets ,landms = face_det(net,image)
    #画在图上
    index = 0
    for det in dets:
        x1,y1,x2,y2,_ = np.int32(det)
        cv2.rectangle(image,(x1,y1),(x2,y2),(255,0,0),2)
        #画人脸关键点
        b = np.int32(landms[index])# 取出单个脸的关键点，并转为整形
        index = index + 1
            # landms
        cv2.circle(image, (b[0], b[1]), 1, (0, 0, 255), 4)#参数：图像、圆心、半径、颜色、线条粗细
        cv2.circle(image, (b[2], b[3]), 1, (0, 0, 255), 4)
        cv2.circle(image, (b[4], b[5]), 1, (0, 0, 255), 4)
        cv2.circle(image, (b[6], b[7]), 1, (0, 0, 255), 4)
        cv2.circle(image, (b[8], b[9]), 1, (0, 0, 255), 4)
        # cv2.circle(image,(b[0],b[1]),1,(0,0,255),4)#参数：图像、圆心
        #数据规定范围
        h,w,_ = image.shape
        if x1 < 0 : x1 = 0
        if x1 > w : x1 = w
        if x2 < 0 : x2 = 0
        if x2 > w : x2 = w
        if y1 < 0 : y1 = 0
        if y1 > h : y1 = h
        if y2 < 0 : y2 = 0
        if y2 > h : y2 = h
        
        #截取，高度方向y1到y2，宽度方向x1:x2,深度方向数据0到3
        # face = image[y1:y2,x1:x2,0:3]
        # print("-----------",x1,y1,x2,y2,index)
        # cv2.imwrite("./images/face_{}.jpg".format(index),face)
    #显示
    cv2.imshow("test",image)
    cv2.waitKey()

